package ColonelBlotto;


import java.util.ArrayList;

import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;

import BinaryGenotype.BinaryGenotype;
import Generic.*;
import IntegerPhenotype.IntegerPhenotype;

public class ColonelBlotto extends EvolutionaryAlgorithm {

	private int numberOfBattles;
	private double reploymentFraction;
	private double strengthLossFraction;
	private static final int DEFAULT_NUMBER_OF_BATTLES = 20;
	private static final double DEFAULT_REPLOYMENT_FRACTION = 1;
	private static final double DEFAULT_STRENGTH_LOSS_FRACTION = .5;
	private static final String AVERAGE_ENTROPY_PROPERTY = "Average Entropy";
	
	protected void initialize(int populationSize,double crossoverRate, double mutationRate){
		numberOfBattles = (int) additionalParameterValues.get(0).doubleValue();
		reploymentFraction = additionalParameterValues.get(1).doubleValue();
		strengthLossFraction = additionalParameterValues.get(2).doubleValue();
		initializeEvolutionaryAlgorithm(populationSize, crossoverRate, mutationRate);
	}
	
	protected Genotype generateRandomGenotype() {
		BinaryGenotype bgtype = new BinaryGenotype(4*numberOfBattles);
		bgtype.randomize();
		return bgtype;
	}

	protected Phenotype developPhenotype(Genotype gtype) {
		return new IntegerPhenotype((BinaryGenotype)gtype, 4);
	}

	protected double calculateFitness(Phenotype ptype) {
		IntegerPhenotype me = (IntegerPhenotype)ptype;
		int mySum = me.getSum();
		
		double fitness = 0.0;
		
		for(Individual ind : children)
		{
			IntegerPhenotype other = (IntegerPhenotype)(ind.getPhenotype());
			if(other==me)
				continue;
			
			int hisSum = other.getSum();
			
			int battlesWon = 0;
			int battlesLost = 0;
			
			double myReployment = 0;
			double hisReployment = 0;
			
			double myStrength = 1;
			double hisStrength = 1;
			
			for(int i = 0; i < numberOfBattles; i++)
			{
				double X = me.getInt(i)/(double)mySum+myReployment;
				double Y =  other.getInt(i)/(double)hisSum+hisReployment;
				
				if(myStrength*X>hisStrength*Y)
				{
					battlesWon++;
					if(X>Y && i!=numberOfBattles-1)
						myReployment+=reploymentFraction*(X-Y)/(numberOfBattles-i-1);
					hisStrength*=strengthLossFraction;
				}
				else if(myStrength*X<hisStrength*Y)
				{
					battlesLost++;
					if(Y>X && i!=numberOfBattles-1)
						hisReployment+=reploymentFraction*(Y-X)/(numberOfBattles-i-1);
					myStrength*=strengthLossFraction;
				}
			}
			
			if(battlesLost*2>numberOfBattles)
				fitness+=0.0;
			else if(battlesWon*2>numberOfBattles)
				fitness+=2.0;
			else
				fitness+=1.0;
		}
		return fitness;
	}

	protected void performParentSelection() {
		children = SelectionPolicy.fitnessProportionate(adults, populationSize);
		//children = SelectionPolicy.boltzmannScaling(adults, populationSize, adults.getGeneration());
	}

	protected void performCrossover(Genotype gtype1, Genotype gtype2) {
		gtype1.onePointCrossover(gtype2);
	}

	protected void performMutation(Genotype genotype) {
		genotype.mutate(mutationRate);
	}

	protected void performReplacement() {
		ReplacementProtocol.fullReplacement(children);
	}
	
	public static void main(String[] args) {
		additionalParameters = new ArrayList<String>();
		additionalParameters.add("Number of Battles");
		additionalParameters.add("Reployment Fraction");
		additionalParameters.add("Strength Loss Fraction");
		
		additionalParameterValues = new ArrayList<Double>();
		additionalParameterValues.add((double) DEFAULT_NUMBER_OF_BATTLES);
		additionalParameterValues.add(DEFAULT_REPLOYMENT_FRACTION);
		additionalParameterValues.add(DEFAULT_STRENGTH_LOSS_FRACTION);
		
		additionalStatisticNames = new ArrayList<String>();
		additionalStatisticNames.add(AVERAGE_ENTROPY_PROPERTY);
		
		EAApplicationFrame2 frame = new EAApplicationFrame2(ColonelBlotto.class);
		frame.setVisible(true);
	}
	
	public double getMaxPossibleFitness() {
		return adults.getSize()*2-2;
	}

	public double getAdditionalStatistic(String string) {
		assert string.equals(AVERAGE_ENTROPY_PROPERTY);
		double ret=getAverageEntropy();
		return ret;
	}

	private double getAverageEntropy() {
		double sum = 0.0;
		for(Individual ind : adults)
			sum+=getEntropy(ind);
		return sum/adults.getSize();
	}
	
	private double getEntropy(Individual ind)
	{
		double ret=0.0;
		IntegerPhenotype iPheno = (IntegerPhenotype)ind.getPhenotype();
		
		double sum = iPheno.getSum();
		
		if(sum==0)
			return 0.0;
		
		for(Integer i:iPheno)
		{
			double normalized = i/sum;
			if(normalized == 0.0)
				continue;
			
			ret-=normalized*(Math.log(normalized)/Math.log(2.0));
		}
		return ret;
	}
	
	public JPanel getBestIndividualPanel() {
		JPanel ret = new JPanel();
		ret.setLayout(new BoxLayout(ret,BoxLayout.X_AXIS));
		IntegerPhenotype ipType = (IntegerPhenotype)adults.getBestIndividual().getPhenotype();
		for(Integer i:ipType)
		{
			ret.add(new JLabel(Integer.toString(   ((int)((i/(double)ipType.getSum())*100))      )+", "));
		}
		return ret;
	}

	public JPanel getTargetPanel() {
		JPanel ret = new JPanel();
		ret.add(new JLabel("No target"));
		return ret;
	}
}
